
/**
 * @file:       buffer.cpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#include "buffer.hpp"

#include <cstring>

Buffer_Controller_ct::Buffer_Controller_ct (const Frame_Config_st & _verify_config) noexcept :
    _m_frame_config {_verify_config} {}

Buffer_Controller_ct::operator std::uint8_t * () noexcept {
    return this->_m_buffer;
}

auto Buffer_Controller_ct::operator+ (unsigned _offset) noexcept -> std::uint8_t * {
    if (_offset >= Buffer_Controller_ct::_buffer_size)
        return nullptr;
    return this->_m_buffer + _offset;
}

auto Buffer_Controller_ct::getBuffer () noexcept -> std::uint8_t * {
    return this->_m_buffer;
}

auto Buffer_Controller_ct::getBufferSize () noexcept -> std::uint32_t {
    return Buffer_Controller_ct::_buffer_size;
}

auto Buffer_Controller_ct::isIndexValid (std::int64_t _index) noexcept -> bool {
    if (this->_m_buffer_index_begin + 0 < this->_m_buffer_index_end + 0) {
        if (this->_m_buffer_index_begin + 0 <= _index && _index <= this->_m_buffer_index_end + 0)
            return true;
    } else {
        if (this->_m_buffer_index_begin + 0 <= _index && _index <= Buffer_Controller_ct::_buffer_size)
            return true;
        if (0 <= _index && _index < this->_m_buffer_index_end + 0)
            return true;
    }
    return false;
}

auto Buffer_Controller_ct::getIndexBegin () const noexcept -> std::uint32_t {
    return static_cast<std::uint32_t> (static_cast<std::int32_t> (this->_m_buffer_index_begin));
}

auto Buffer_Controller_ct::getIndexBeginAddr () noexcept -> std::uint8_t * {
    return &(this->_m_buffer[this->_m_buffer_index_begin]);
}

auto Buffer_Controller_ct::getIndexEnd () const noexcept -> std::uint32_t {
    return static_cast<std::uint32_t> (static_cast<std::int32_t> (this->_m_buffer_index_end));
}

auto Buffer_Controller_ct::getIndexEndAddr () noexcept -> std::uint8_t * {
    return &(this->_m_buffer[this->_m_buffer_index_end]);
}

auto Buffer_Controller_ct::getBufferLoadSize () const noexcept -> std::uint32_t {
    if (this->getIndexBegin () <= this->getIndexEnd ())
        return this->getIndexEnd () - this->getIndexBegin ();
    else return this->getIndexEnd () + Buffer_Controller_ct::_buffer_size - this->getIndexBegin ();
}

auto Buffer_Controller_ct::getBufferFreeSize () const noexcept -> std::uint32_t {
    return Buffer_Controller_ct::_buffer_size - this->getBufferLoadSize ();
}

auto Buffer_Controller_ct::isBufferFull () noexcept -> bool {
    return static_cast<std::int64_t> (this->_m_buffer_index_begin) ==
           static_cast<std::int64_t> (this->_m_buffer_index_end + 1);
}

auto Buffer_Controller_ct::isBufferEmpty () noexcept -> bool {
    return static_cast<std::int64_t> (this->_m_buffer_index_begin) ==
           static_cast<std::int64_t> (this->_m_buffer_index_end);
}

auto Buffer_Controller_ct::getLock () noexcept -> Buffer_Controller_ct::Locker_Mutex_ft & {
    return this->_m_lock;
}

auto Buffer_Controller_ct::getUnLock () noexcept -> Buffer_Controller_ct::Locker_Mutex_ft & {
    return this->_m_unlock;
}

auto Buffer_Controller_ct::frameConfig () noexcept -> struct Frame_Config_st & {
    return this->_m_frame_config;
}

auto Buffer_Controller_ct::clear () noexcept -> void {
    this->_m_buffer_index_begin = 0;
    this->_m_buffer_index_end   = 0;
    std::memset (this->_m_buffer, 0, Buffer_Controller_ct::_buffer_size);
}

auto Buffer_Controller_ct::push (std::uint8_t _data[], std::uint32_t _length) noexcept -> std::uint32_t {
    if (_data == nullptr)
        return 0;
    std::uint32_t _counts = 0;
    this->lock ();
    for (std::uint32_t i = 0; i < _length; i++) {
        if (this->isBufferFull ())
            break;
        this->_m_buffer[this->_m_buffer_index_end++] = _data[i];
        _counts++;
    }
    this->unlock ();
    return _counts;
}

#define FIXED_CALC(STDINT)                                                                                                                                                                                                                                                                                                       \
    do {                                                                                                                                                                                                                                                                                                                         \
        Verification_Calculator_ct<STDINT> * _calc_inst = (dynamic_cast<Verification_Calculator_ct<STDINT> *> (_config._m_check._calculate_instance));                                                                                                                                                                           \
        if (!_calc_inst) break;                                                                                                                                                                                                                                                                                                  \
        STDINT _check_result_calc = 0;                                                                                                                                                                                                                                                                                           \
        STDINT _check_result_recv = 0;                                                                                                                                                                                                                                                                                           \
        _calc_inst->resetCurrent ();                                                                                                                                                                                                                                                                                             \
        for (_index_buffer = (_head + _config._m_check._offset + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size, _index_check_counts = 0; (((_index_buffer + 0) != (this->_m_buffer_index_end + 1)) && (_index_check_counts < (_config._m_check._size_data))); _index_buffer++, _index_check_counts++) \
            _calc_inst->accumulate (&this->_m_buffer[_index_buffer], 1);                                                                                                                                                                                                                                                         \
        _check_result_calc = _calc_inst->accumulated ();                                                                                                                                                                                                                                                                         \
        for (STDINT i = 0; i < sizeof (STDINT); i++)                                                                                                                                                                                                                                                                             \
            (reinterpret_cast<std::uint8_t *> (&_check_result_recv))[i] = this->_m_buffer[(i + _head + _config._m_frame_length - _config._m_check._size_result + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size];                                                                                      \
        if (!_byte_order_consistency)                                                                                                                                                                                                                                                                                            \
            _check_result_calc = Bytes_Calculator_st::reverseByteOrder (_check_result_calc);                                                                                                                                                                                                                                     \
        if (_check_result_calc == _check_result_recv)                                                                                                                                                                                                                                                                            \
            _check_metch = true;                                                                                                                                                                                                                                                                                                 \
        else _check_metch = false;                                                                                                                                                                                                                                                                                               \
    } while (false)

#define UNFIXED_CALC(STDINT)                                                                                                                                                                                                                                                                                                     \
    do {                                                                                                                                                                                                                                                                                                                         \
        Verification_Calculator_ct<STDINT> * _calc_inst = (dynamic_cast<Verification_Calculator_ct<STDINT> *> (_config._m_check._calculate_instance));                                                                                                                                                                           \
        if (!_calc_inst) break;                                                                                                                                                                                                                                                                                                  \
        STDINT _check_result_calc = 0;                                                                                                                                                                                                                                                                                           \
        STDINT _check_result_recv = 0;                                                                                                                                                                                                                                                                                           \
        _calc_inst->resetCurrent ();                                                                                                                                                                                                                                                                                             \
        for (_index_buffer = (_head + _config._m_check._offset + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size, _index_check_counts = 0; (((_index_buffer + 0) != (this->_m_buffer_index_end + 1)) && (_index_check_counts < (_config._m_check._size_data))); _index_buffer++, _index_check_counts++) \
            _calc_inst->accumulate (&this->_m_buffer[_index_buffer], 1);                                                                                                                                                                                                                                                         \
        _check_result_calc = _calc_inst->accumulated ();                                                                                                                                                                                                                                                                         \
        for (STDINT i = 0; i < sizeof (STDINT); i++)                                                                                                                                                                                                                                                                             \
            (reinterpret_cast<std::uint8_t *> (&_check_result_recv))[i] = this->_m_buffer[(i + _tail - _config._m_check._size_result + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size];                                                                                                                \
        if (!_byte_order_consistency)                                                                                                                                                                                                                                                                                            \
            _check_result_calc = Bytes_Calculator_st::reverseByteOrder (_check_result_calc);                                                                                                                                                                                                                                     \
        if (_check_result_calc == _check_result_recv)                                                                                                                                                                                                                                                                            \
            _check_metch = true;                                                                                                                                                                                                                                                                                                 \
        else _check_metch = false;                                                                                                                                                                                                                                                                                               \
    } while (false)

auto Buffer_Controller_ct::pop (std::uint8_t _data[], std::uint32_t _start, std::uint32_t _max_size) noexcept -> std::uint32_t {
    if (_data == nullptr)
        return 0;
    if (this->_m_frame_config._m_check._type != Frame_Config_st::Check_st::Type_et::Check_None)
        if (this->_m_frame_config._m_check._calculate_instance == nullptr)
            return 0;

    auto   _old_start              = _start;
    auto & _config                 = this->_m_frame_config;
    bool   _byte_order_consistency = static_cast<bool> (_config._m_fram_byte_order == Bytes_Calculator_st::_platform_byte_order);

    int _index_check_counts;
    int _head, __attribute__ ((unused)) _head_next;
    int _tail, __attribute__ ((unused)) _tail_next;
    bool                         _find_frame;
    bool                         _check_metch;
    Advanced_Circular_Stepper_ct _index_buffer;

    this->lock ();
    do {
        _index_check_counts = 0;
        _head               = this->findHead ();
        _tail               = this->findTail ();
        if (_head != -1)
            this->_m_buffer_index_begin = _head,
            _head_next                  = this->nextHead ((_head + 1 + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size);
        else _head_next = -1;

        _find_frame   = false;
        _check_metch  = false;
        _index_buffer = this->_m_buffer_index_begin;

        if (_config._m_frame_fix == Frame_Config_st::Fix_et::Fixed) {
            if ((this->getBufferLoadSize () < static_cast<std::uint32_t> (_config._m_frame_length)) || ((_head_next != -1) && (((_head_next - _head + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size) < _config._m_frame_length))) {
                if (_head_next != -1)
                    this->_m_buffer_index_begin = _head_next;
                this->unlock ();
                return (_start - _old_start);
            }
            if (_config._m_check._type != Frame_Config_st::Check_st::Type_et::Check_None) {
                if (_config._m_check._size_result == 1) FIXED_CALC (std::uint8_t);
                if (_config._m_check._size_result == 2) FIXED_CALC (std::uint16_t);
                if (_config._m_check._size_result == 4) FIXED_CALC (std::uint32_t);
                if (_config._m_check._size_result == 8) FIXED_CALC (std::uint64_t);
            } else _check_metch = true;
            _find_frame = _check_metch;
            if (_find_frame) {
                for (_index_check_counts = 0, _index_buffer = _head; ((_start - _old_start < _max_size) && (_index_check_counts < _config._m_frame_length)); _index_check_counts++, _index_buffer++)
                    _data[_start++] = this->_m_buffer[_index_buffer];
                _index_buffer -= 1;
            }
            this->_m_buffer_index_begin = _index_buffer;
        } else if (_config._m_frame_fix == Frame_Config_st::Fix_et::UnFixed) {

            // 帧内的数据存在长度信息
            std::uint64_t _record_size = 0;
            if (_config._m_check._record_size_length != 0) {
                if (_byte_order_consistency) {
                    for (auto i = 0; i < _config._m_check._record_size_length; ++i)
                        _record_size |= static_cast<std::uint64_t> (this->_m_buffer[(_head + _config._m_check._record_size_offset + i) % Buffer_Controller_ct::_buffer_size]) << (8 * i);
                } else {
                    for (auto i = 0; i < _config._m_check._record_size_length; ++i)
                        _record_size |= static_cast<std::uint64_t> (this->_m_buffer[(_head + _config._m_check._record_size_offset + i) % Buffer_Controller_ct::_buffer_size]) << (8 * (_config._m_check._record_size_length - 1 - i));
                }
            }

            // 对于不定长帧, 如果同时设置了帧头和帧尾, 首先根据帧头和帧尾在缓冲区中的位置自动推导整帧长度
            if (_config._m_tail._section_size * _config._m_tail._section_count != 0 && _tail != -1)
                _config._m_frame_length = (_tail - _head + Buffer_Controller_ct::_buffer_size + _config._m_tail._section_size) % Buffer_Controller_ct::_buffer_size;

            // 或者如果 (帧的内部) 记录了 "数据长度" 信息并且同时设置了 "其他长度", 则将整帧长度推导为 "数据长度" + "其他长度"
            else if (_config._m_check._record_size_length != 0 && _config._m_check._size_other != 0)
                _config._m_frame_length = _record_size + _config._m_check._size_other;


            // 添加了全帧校验的支持, 如果校验偏移量是 0, 则表示全帧校验, 这时候 "数据长度" == "整帧长度" - "校验结果长度"
            if (_config._m_check._offset != 0) {

                // 根据整帧长度和 "其他长度" 自动推导 "数据长度"
                if (_config._m_check._size_other != 0)
                    _config._m_check._size_data = _config._m_frame_length - _config._m_check._size_other;

                // 如果 (帧的内部) 记录了 "数据长度" 信息, 则直接使用
                else if (_config._m_check._record_size_length != 0)
                    _config._m_check._size_data = static_cast<std::uint16_t> (_record_size & 0xFFFF);

            } else _config._m_check._size_data = _config._m_frame_length - _config._m_check._size_result;


            if ((this->getBufferLoadSize () < static_cast<std::uint32_t> (_config._m_frame_length)) || ((_head_next != -1) && (((_head_next - _head + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size) < _config._m_frame_length))) {
                if (_head_next != -1)
                    this->_m_buffer_index_begin = _head_next;
                this->unlock ();
                return (_start - _old_start);
            }
            if (_config._m_check._type != Frame_Config_st::Check_st::Type_et::Check_None) {
                if (_config._m_check._size_result == 1) UNFIXED_CALC (std::uint8_t);
                if (_config._m_check._size_result == 2) UNFIXED_CALC (std::uint16_t);
                if (_config._m_check._size_result == 4) UNFIXED_CALC (std::uint32_t);
                if (_config._m_check._size_result == 8) UNFIXED_CALC (std::uint64_t);
            } else _check_metch = true;
            _find_frame = _check_metch;
            if (_find_frame) {
                for (_index_check_counts = 0, _index_buffer = _head; ((_start - _old_start < _max_size) && ((_index_buffer + 0) != (_tail + _config._m_tail._section_size + Buffer_Controller_ct::_buffer_size) % Buffer_Controller_ct::_buffer_size)); _index_check_counts++, _index_buffer++)
                    _data[_start++] = this->_m_buffer[_index_buffer];
                _index_buffer -= 1;
            }
            this->_m_buffer_index_begin = _index_buffer;
        }
    } while (static_cast<bool> (this->_m_frame_config._m_return));
    this->unlock ();
    return (_start - _old_start);
}

#undef FIXED_CALC
#undef UNFIXED_CALC

auto Buffer_Controller_ct::pop () noexcept -> bool {
    bool _match = true;
    this->lock ();
    if (!this->isBufferEmpty ())
        this->_m_buffer_index_begin += 1;
    if (this->getBufferLoadSize () < this->_m_frame_config._m_header._section_size)
        return false;
    if (this->_m_frame_config._m_header._section_count * this->_m_frame_config._m_header._section_size == 0)
        return false;

    for (auto i = 0; this->_m_buffer_index_begin + i != this->_m_buffer_index_end - this->_m_frame_config._m_header._section_size; i++) {

        for (auto c = 0; c < this->_m_frame_config._m_header._section_count; c++) {
            _match = true;
            for (auto j = c * this->_m_frame_config._m_header._section_size; j < (c + 1) * this->_m_frame_config._m_header._section_size; j++) {
                if (this->_m_buffer[this->_m_buffer_index_begin + i + (j % this->_m_frame_config._m_header._section_size)] != this->_m_frame_config._m_header._section_data[j]) {
                    _match = false;
                    break;
                }
            }
            if (_match) {
                this->_m_buffer_index_begin += i;
                this->unlock ();
                return true;
            }
        }
    }
    this->_m_buffer_index_begin -= 1;
    this->unlock ();
    return false;
}

auto Buffer_Controller_ct::findHead () noexcept -> int {
    int  _index = -1;
    bool _match = true;
    this->lock ();
    if (this->getBufferLoadSize () < this->_m_frame_config._m_header._section_size)
        return _index;
    if (this->_m_frame_config._m_header._section_count * this->_m_frame_config._m_header._section_size == 0)
        return _index;

    for (auto i = 0; this->_m_buffer_index_begin + i != this->_m_buffer_index_end - (this->_m_frame_config._m_header._section_size - 1); i++) {
        for (auto c = 0; c < this->_m_frame_config._m_header._section_count; c++) {
            _match = true;
            for (auto j = c * this->_m_frame_config._m_header._section_size; j < (c + 1) * this->_m_frame_config._m_header._section_size; j++) {
                if (this->_m_buffer[this->_m_buffer_index_begin + (i + (j % this->_m_frame_config._m_header._section_size))] != this->_m_frame_config._m_header._section_data[j]) {
                    _match = false;
                    break;
                }
            }
            if (_match) {
                _index = this->_m_buffer_index_begin + i;
                this->unlock ();
                return _index;
            }
        }
    }
    this->unlock ();
    return _index;
}

auto Buffer_Controller_ct::nextHead (std::int64_t _relative) noexcept -> int {
    int  _index = -1;
    bool _match = true;
    this->lock ();
    if (this->getBufferLoadSize () < this->_m_frame_config._m_header._section_size)
        return _index;
    if (this->_m_frame_config._m_header._section_count * this->_m_frame_config._m_header._section_size == 0)
        return _index;
    if (!this->isIndexValid (_relative))
        return _index;

    int _index_bak              = this->_m_buffer_index_begin;
    this->_m_buffer_index_begin = _relative;

    for (auto i = 0; this->_m_buffer_index_begin + i != this->_m_buffer_index_end - (this->_m_frame_config._m_header._section_size - 1); i++) {
        for (auto c = 0; c < this->_m_frame_config._m_header._section_count; c++) {
            _match = true;
            for (auto j = c * this->_m_frame_config._m_header._section_size; j < (c + 1) * this->_m_frame_config._m_header._section_size; j++) {
                if (this->_m_buffer[this->_m_buffer_index_begin + (i + (j % this->_m_frame_config._m_header._section_size))] != this->_m_frame_config._m_header._section_data[j]) {
                    _match = false;
                    break;
                }
            }
            if (_match) {
                _index                      = this->_m_buffer_index_begin + i;
                this->_m_buffer_index_begin = _index_bak;
                this->unlock ();
                return _index;
            }
        }
    }
    this->_m_buffer_index_begin = _index_bak;
    this->unlock ();
    return _index;
}

auto Buffer_Controller_ct::findTail () noexcept -> int {
    int  _index = -1;
    bool _match = true;
    this->lock ();
    if (this->getBufferLoadSize () < this->_m_frame_config._m_tail._section_size)
        return _index;
    if (this->_m_frame_config._m_tail._section_count * this->_m_frame_config._m_tail._section_size == 0)
        return _index;

    for (auto i = 0; this->_m_buffer_index_begin + i != this->_m_buffer_index_end - (this->_m_frame_config._m_tail._section_size - 1); i++) {

        for (auto c = 0; c < this->_m_frame_config._m_tail._section_count; c++) {
            _match = true;
            for (auto j = c * this->_m_frame_config._m_tail._section_size; j < (c + 1) * this->_m_frame_config._m_tail._section_size; j++) {
                if (this->_m_buffer[this->_m_buffer_index_begin + (i + (j % this->_m_frame_config._m_tail._section_size))] != this->_m_frame_config._m_tail._section_data[j]) {
                    _match = false;
                    break;
                }
            }
            if (_match) {
                _index = this->_m_buffer_index_begin + i;
                this->unlock ();
                return _index;
            }
        }
    }
    this->unlock ();
    return _index;
}

auto Buffer_Controller_ct::nextTail (std::int64_t _relative) noexcept -> int {
    int  _index = -1;
    bool _match = true;
    this->lock ();
    if (this->getBufferLoadSize () < this->_m_frame_config._m_tail._section_size)
        return _index;
    if (this->_m_frame_config._m_tail._section_count * this->_m_frame_config._m_tail._section_size == 0)
        return _index;
    if (!this->isIndexValid (_relative))
        return _index;

    int _index_bak              = this->_m_buffer_index_begin;
    this->_m_buffer_index_begin = _relative;

    for (auto i = 0; this->_m_buffer_index_begin + i != this->_m_buffer_index_end - (this->_m_frame_config._m_tail._section_size - 1); i++) {

        for (auto c = 0; c < this->_m_frame_config._m_tail._section_count; c++) {
            _match = true;
            for (auto j = c * this->_m_frame_config._m_tail._section_size; j < (c + 1) * this->_m_frame_config._m_tail._section_size; j++) {
                if (this->_m_buffer[this->_m_buffer_index_begin + (i + (j % this->_m_frame_config._m_tail._section_size))] != this->_m_frame_config._m_tail._section_data[j]) {
                    _match = false;
                    break;
                }
            }
            if (_match) {
                _index                      = this->_m_buffer_index_begin + i;
                this->_m_buffer_index_begin = _index_bak;
                this->unlock ();
                return _index;
            }
        }
    }
    this->_m_buffer_index_begin = _index_bak;
    this->unlock ();
    return _index;
}

auto Buffer_Controller_ct::lock () noexcept -> void {
    if (this->_m_lock != nullptr)
        this->_m_lock ();
}

auto Buffer_Controller_ct::unlock () noexcept -> void {
    if (this->_m_unlock != nullptr)
        this->_m_unlock ();
}
